//
//  Untitled.swift
//  PLDownload
//
//  Created by bin hu on 2025/7/7.
//
import Foundation

class DownloadManager {
    
    static let shared = DownloadManager()
    
    var tasks: [DownloadTask] = []
    var activeDownloads: Int = 0
    var diskSpaceWarning: Bool = false
    
    let maxConcurrentDownloads = 3
    let fileManager = FileSystemManager.shared
    
    private var handlers: [UUID: ProtocolHandler] = [:]
    private var timer: Timer?
    
    init() {
        startDiskSpaceMonitoring()
    }
    
    func addTask(url: URL, title: String? = nil) {
        let fileName = title ?? url.lastPathComponent
        let fileExtension = url.pathExtension.lowercased()
        
        let task = DownloadTask(
            id: UUID(),
            url: url,
            title: fileName,
            fileType: fileExtension
        )
        
        tasks.append(task)
        startNextTasksIfPossible()
    }
    
    func startTask(id: UUID) {
        guard let index = tasks.firstIndex(where: { $0.id == id }),
              tasks[index].state == .queued || tasks[index].state == .paused else {
            return
        }
        
        let task = tasks[index]
        var handler: ProtocolHandler
        
        switch task.fileType {
            case "m3u8":
                handler = M3U8Handler(task: task)
            case "FTP":
                handler = FTPHandler(task: task)
            default:
                handler = HTTPHandler(task: task)
        }
        
        handlers[task.id] = handler
        handler.startDownload(manager: self)
    }
    
    func pauseTask(id: UUID) {
        guard let handler = handlers[id] else { return }
        handler.pauseDownload()
        updateTaskState(id: id, state: .paused)
    }
    
    func resumeTask(id: UUID) {
        guard let handler = handlers[id] else {
            startTask(id: id)
            return
        }
        handler.resumeDownload()
    }
    
    func cancelTask(id: UUID) {
        guard let handler = handlers[id] else { return }
        handler.cancelDownload()
        fileManager.clearTempDirectory(for: tasks.first(where: { $0.id == id })!)
        handlers.removeValue(forKey: id)
        updateTaskState(id: id, state: .queued)
    }
    
    func removeTask(id: UUID) {
        cancelTask(id: id)
        tasks.removeAll(where: { $0.id == id })
    }
    
    func updateTaskState(id: UUID, state: DownloadState) {
        if let index = tasks.firstIndex(where: { $0.id == id }) {
            tasks[index].state = state
            updateActiveDownloadsCount()
        }
    }
    
    func updateTask(id: UUID, state: DownloadState, localPath: URL) {
        if let index = tasks.firstIndex(where: { $0.id == id }) {
            tasks[index].state = state
            tasks[index].localPath = localPath
            updateActiveDownloadsCount()
        }
    }
    
    private func startNextTasksIfPossible() {
        let queuedTasks = tasks.filter { task in
            if case .queued = task.state {
                return true
            }
            return false
        }
        
        let availableSlots = maxConcurrentDownloads - activeDownloads
        
        for task in queuedTasks.prefix(availableSlots) {
            startTask(id: task.id)
        }
    }
    
    private func updateActiveDownloadsCount() {
        activeDownloads = tasks.reduce(0) { count, task in
            if case .downloading = task.state {
                return count + 1
            }
            return count
        }
    }
    
    private func startDiskSpaceMonitoring() {
        timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] _ in
            self?.checkDiskSpace()
        }
    }
    
    private func checkDiskSpace() {
        let fileSystemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory())
        let freeSpace = (fileSystemAttributes?[.systemFreeSize] as? NSNumber)?.int64Value ?? 0
        diskSpaceWarning = freeSpace < 100 * 1024 * 1024 // 100 MB
    }
    
    func openFile(id: UUID) {
        guard let task = tasks.first(where: { $0.id == id }),
              let path = task.localPath else {
            return
        }
        fileManager.openFile(at: path)
    }
    
}

extension DownloadManager {
    func handleError(_ error: Error, for task: DownloadTask) {
        task.state = .failed(error)
        logError(error)
        retryOrAbort(task: task)
    }
}

extension DownloadManager {
    func addFTPTask(url: URL, credential: URLCredential? = nil) {
        var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        if let credential = credential {
            components?.user = credential.user
            components?.password = credential.password
        }
        
        guard let ftpURL = components?.url else { return }
        addTask(url: ftpURL)
    }
    
    func handleFTPStreamEvent(_ stream: Stream, handle eventCode: Stream.Event) {
        switch eventCode {
        case .openCompleted:
            updateTaskState(id: currentTaskID, state: .downloading)
        case .hasBytesAvailable:
            readFTPData(stream as! InputStream)
        case .errorOccurred:
            updateTaskState(id: currentTaskID, state: .failed)
        case .endEncountered:
            completeDownload()
        default: break
        }
    }
    
    private func readFTPData(_ inputStream: InputStream) {
        let bufferSize = 1024
        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        defer { buffer.deallocate() }
        
        while inputStream.hasBytesAvailable {
            let bytesRead = inputStream.read(buffer, maxLength: bufferSize)
            if bytesRead > 0 {
                writeToFile(buffer: buffer, length: bytesRead)
            }
        }
    }
}

